package example;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: fzykd
 * Date: 2022-10-16
 * Time: 19:08
 */
//先创建一个类 来描述任务
class MyTask implements Comparable<MyTask>{
    //表示要执行的任务
    private Runnable runnable;
    //认识什么什么时间执行
    private long time;

    //这里的时间是一个相对时间
    public MyTask(Runnable runnable, long after) {
        this.runnable = runnable;
        this.time = after+System.currentTimeMillis();
    }
    public void run(){
        runnable.run();
    }
    public long getTime(){
        return this.time;
    }

    //要实现以恶搞compareTo接口 固定写法
    //不然程序不知道用什么来比较
    public int compareTo(MyTask o){
        return (int) (this.time-o.time);
    }

}


//自主实现一个定时器
class MyTimer {
    //描述之后是组织任务
    //用一个数据结构来组织任务 带有线程安全的阻塞队列
    PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    //布置任务 将任务put

    //处理线程安全问题
    private Object locker = new Object();
    public void schedule(Runnable runnable, long delay) {
        MyTask myTask = new MyTask(runnable, delay);
        queue.put(myTask);
        //每次成功的插入之后 检查 新的任务是否是等待时间最短的
        synchronized (locker){
            locker.notify();
        }
    }

    //3.知道时间到了的任务 要有一个线程 不断的去检查
    //检查线程是 写在了自身的构造函数当中
    public MyTimer(){
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    //先取出队首元素
                    MyTask myTask = queue.take();
                    long curTime = System.currentTimeMillis();
                    //这里比较任务的时间无法获取 在task里面另写一个getTime
                    if (curTime < myTask.getTime()) {
                        //时间没到 那么再把任务放回到队列当中
                        queue.put(myTask);
                        //检查时间没到 又放回去 设定一个等待时间
                        synchronized (locker){
                            locker.wait(myTask.getTime() - curTime);
                        }
                    } else {
                        myTask.run();
                        //时间和run方法 都要额外获取
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}


public class Time{
    //1.基础代码写完 存在两个缺陷 第一个Comparable MyTask没有指定比较规则

    //2.第二个缺陷就是 如果队列中有任务 但是时间没到 那么单独那个线程就一直去读操作 在“忙等“
    //那么就需要指定一个等待时间 时间到了 就执行 不能使用sleep 因为中途不能被唤醒 使用wait
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        },3000);
    }
}
